//-----------------------------------------------------------------------------
// Torque Game Engine 
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------

// This inventory system is totally scripted, no C++ code involved.
// It uses object datablock names to track inventory and is generally
// object type, or class, agnostic.  In other words, it will inventory
// any kind of ShapeBase object, though the throw method does assume
// that the objects are small enough to throw :)
//
// For a ShapeBase object to support inventory, it must have an array
// of inventory max values:
//
//    %this.maxInv[GunAmmo] = 100;
//    %this.maxInv[SpeedGun] = 1;
//
// where the names "SpeedGun" and "GunAmmo" are datablocks.
//
// For objects to be inventoriable, they must provide a set of inventory
// callback methods, mainly:
//
//    onUse
//    onThrow
//    onPickup
//
// Example methods are given further down.  The item.cs file also contains
// example inventory items.


//-----------------------------------------------------------------------------
// Inventory server commands
//-----------------------------------------------------------------------------

function serverCmdUse(%client,%data)
{
   %client.getControlObject().use(%data);
}

//-----------------------------------------------------------------------------
// ShapeBase inventory support
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------

function ShapeBase::use(%this,%data)
{
   // Use an object in the inventory.
   if (%this.getInventory(%data) > 0)
      return %data.onUse(%this);
   return false;
}

function ShapeBase::throw(%this,%data,%amount)
{
   // Throw objects from inventory. The onThrow method is
   // responsible for decrementing the inventory.
   if (%this.getInventory(%data) > 0) {
      %obj = %data.onThrow(%this,%amount);
      if (%obj) {
         %this.throwObject(%obj);
         return true;
      }
   }
   return false;
}

function ShapeBase::pickup(%this,%obj,%amount)
{  
   // This method is called to pickup an object and add it
   // to the inventory. The datablock onPickup method is actually
   // responsible for doing all the work, including incrementing
   // the inventory.
   %data = %obj.getDatablock();

   // Try and pickup the max if no value was specified
   if (%amount $= "")
      %amount = %this.maxInventory(%data) - %this.getInventory(%data);

   // The datablock does the work...
   if (%amount < 0)
      %amount = 0;
   if (%amount)
      return %data.onPickup(%obj,%this,%amount);
   return false;
}


//-----------------------------------------------------------------------------

function ShapeBase::maxInventory(%this,%data)
{
   // If there is no limit defined, we assume 0
   return %this.getDatablock().maxInv[%data.getName()];
}

function ShapeBase::incInventory(%this,%data,%amount)
{
   // Increment the inventory by the given amount.  The return value
   // is the amount actually added, which may be less than the
   // requested amount due to inventory restrictions.
   %max = %this.maxInventory(%data);
   %total = %this.inv[%data.getName()];
   if (%total < %max) {
      if (%total + %amount > %max)
         %amount = %max - %total;
      %this.setInventory(%data,%total + %amount);
      return %amount;
   }
   return 0;
}

function ShapeBase::decInventory(%this,%data,%amount)
{
   // Decrement the inventory by the given amount. The return value
   // is the amount actually removed.
   %total = %this.inv[%data.getName()];
   if (%total > 0) {
      if (%total < %amount)
         %amount = %total;
      %this.setInventory(%data,%total - %amount);
      return %amount;
   }
   return 0;
}


//-----------------------------------------------------------------------------

function ShapeBase::getInventory(%this,%data)
{
   // Return the current inventory amount
   return %this.inv[%data.getName()];
}

function ShapeBase::setInventory(%this,%data,%value)
{
   // Set the inventory amount for this datablock and invoke
   // inventory callbacks.  All changes to inventory go through this
   // single method.

   // Impose inventory limits
   if (%value < 0)
      %value = 0;
   else {
      %max = %this.maxInventory(%data);
      if (%value > %max)
         %value = %max;
   }

   // Set the value and invoke object callbacks
   %name = %data.getName();
   if (%this.inv[%name] != %value) 
   {
      %this.inv[%name] = %value;
      %data.onInventory(%this,%value);
      %this.getDataBlock().onInventory(%data,%value);
   }
   return %value;
}


//-----------------------------------------------------------------------------

function ShapeBase::clearInventory(%this)
{
   // To be filled in...
}


//-----------------------------------------------------------------------------

function ShapeBase::throwObject(%this,%obj)
{
   // Throw the given object in the direction the shape is looking.
   // The force value is hardcoded according to the current default
   // object mass and mission gravity (20m/s^2).
   %throwForce = %this.throwForce;
   if (!%throwForce)
      %throwForce = 20;

   // Start with the shape's eye vector...
   %eye = %this.getEyeVector();
   %vec = vectorScale(%eye, %throwForce);

   // Add a vertical component to give the object a better arc
   %verticalForce = %throwForce / 2;
   %dot = vectorDot("0 0 1",%eye);
   if (%dot < 0)
      %dot = -%dot;
   %vec = vectorAdd(%vec,vectorScale("0 0 " @ %verticalForce,1 - %dot));

   // Add the shape's velocity
   %vec = vectorAdd(%vec,%this.getVelocity());

   // Set the object's position and initial velocity
   %pos = getBoxCenter(%this.getWorldBox());
   %obj.setTransform(%pos);
   %obj.applyImpulse(%pos,%vec);

   // Since the object is thrown from the center of the
   // shape, the object needs to avoid colliding with it's
   // thrower.
   %obj.setCollisionTimeout(%this);
}


//-----------------------------------------------------------------------------
// Callback hooks invoked by the inventory system
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// ShapeBase object callbacks invoked by the inventory system

function ShapeBase::onInventory(%this, %data, %value)
{
   // Invoked on ShapeBase objects whenever their inventory changes
   // for the given datablock.
}


//-----------------------------------------------------------------------------
// ShapeBase datablock callback invoked by the inventory system.

function ShapeBaseData::onUse(%this,%user)
{
   // Invoked when the object uses this datablock, should return
   // true if the item was used.
   return false;
}

function ShapeBaseData::onThrow(%this,%user,%amount)
{
   // Invoked when the object is thrown.  This method should
   // construct and return the actual mission object to be
   // physically thrown.  This method is also responsible for
   // decrementing the user's inventory.
   return 0;
}

function ShapeBaseData::onPickup(%this,%obj,%user,%amount)
{
   // Invoked when the user attempts to pickup this datablock object.
   // The %amount argument is the space in the user's inventory for
   // this type of datablock.  This method is responsible for
   // incrementing the user's inventory is something is addded.
   // Should return true if something was added to the inventory.
   return false;
}

function ShapeBaseData::onInventory(%this,%user,%value)
{
   // Invoked whenever an user's inventory total changes for
   // this datablock.
}

function ShapeBase::hasInventory(%this, %data)
{
   return (%this.inv[%data] > 0);
}
